package com.rslai.base.tool.servertest;

import com.rslai.base.tool.servertest.command.CoberturaCommand;
import com.rslai.base.tool.servertest.command.StepCommand;
import com.rslai.base.tool.servertest.context.Context;
import com.rslai.base.tool.servertest.model.DataDrivenTestCase;
import com.rslai.base.tool.servertest.model.KeyValueStore;
import com.rslai.base.tool.servertest.model.TestCase;
import com.rslai.base.tool.servertest.model.TestSuite;
import com.rslai.base.tool.servertest.reporter.Reporter;
import com.rslai.base.tool.servertest.statement.ServertestStatement;
import com.rslai.base.tool.servertest.util.CommandUtil;
import com.rslai.base.tool.servertest.util.ParameterUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.junit.internal.AssumptionViolatedException;
import org.junit.internal.runners.model.EachTestNotifier;
import org.junit.runner.Description;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;

public class TestSuiteRunner extends BlockJUnit4ClassRunner {
    private TestSuite testSuite;
    private Context suitContext;
    private Reporter reporter = null;

    public TestSuiteRunner(Class clazz, TestSuite testSuite, Context suitContext, Reporter rpt)
            throws InitializationError {
        super(clazz);
        this.testSuite = testSuite;
        this.reporter = rpt;
        this.suitContext = suitContext;
    }

    public void run(RunNotifier notifier) {
        try {
            this.reporter.report(this.testSuite);
            super.run(notifier);
        } finally {
            this.reporter.done();
        }
    }

    protected List<FrameworkMethod> computeTestMethods() {
        if (isTestWithFile()) {
            List frameworkMethodList = new ArrayList();
            addTestCase(frameworkMethodList, this.testSuite.getBackGrounds());
            addTestCase(frameworkMethodList, this.testSuite.getBeforeSuite());

            withBeforeAndAfterCommand(frameworkMethodList);

            addTestCase(frameworkMethodList, this.testSuite.getAfterSuite());
            return frameworkMethodList;
        }
        return super.computeTestMethods();
    }

    private StepCommand buildStepCommand(List<TestCase> testCases) {
        if ((testCases == null) || (testCases.size() < 1)) {
            return null;
        }
        StepCommand command = null;
        for (TestCase tc : testCases) {
            command = CommandUtil.concatCommand(command, tc.getBodyCommand().cloneCommand());
        }
        return command;
    }

    private void withBeforeAndAfterCommand(List<FrameworkMethod> frameworkMethodList) {
        List<TestCase> testCases = this.testSuite.getTestCases();
        if ((testCases == null) || (testCases.size() < 1)) {
            return;
        }
        for (TestCase testCase : testCases) {
            String caseId = testCase.getId();
            StepCommand stepCommand = new CoberturaCommand(caseId);
            testCase.setBeforeCommand(addCoberturaCommand(stepCommand, buildStepCommand(this.testSuite.getBeforeCase())));
            testCase.setAfterCommand(buildStepCommand(this.testSuite.getAfterCase()));
            if ((testCase instanceof DataDrivenTestCase)) {
                addDataDrivenTestCases(frameworkMethodList, (DataDrivenTestCase) testCase, this.suitContext);
            } else {
                Context caseContext = new Context(this.suitContext);
                frameworkMethodList.add(new ServertestFrameworkMethod(null, testCase, caseContext));
            }
        }
    }

    private StepCommand addCoberturaCommand(StepCommand coberturaCommand, StepCommand beforeCommand) {
        StepCommand command = coberturaCommand.cloneCommand();
        command.setNextCommand(beforeCommand);
        return command;
    }

    private void addDataDrivenTestCases(List frameworkMethodList, DataDrivenTestCase testCase, Context suiteContext) {
        List examples = testCase.getExamples();
        int index = 0;
        Context caseContext;
        TestCase newTestCase;
        for (Iterator i$ = examples.iterator(); i$.hasNext(); frameworkMethodList.add(new ServertestFrameworkMethod(null, newTestCase, caseContext))) {
            Map data = (Map) i$.next();
            caseContext = new Context(suiteContext);
            createCaseContext(data, suitContext, caseContext);
            newTestCase = testCase.clone();
            newTestCase.setDesc((new StringBuilder()).append(newTestCase.getDesc()).append("_").append(index++).toString());
        }
    }

    private void createCaseContext(Map data, Context suitContext, Context caseContext) {
        List params = new ArrayList();
        for (Iterator i$ = data.entrySet().iterator(); i$.hasNext(); ) {
            java.util.Map.Entry entry = (java.util.Map.Entry) i$.next();
            if ("null".equals(entry.getValue())) {
                String name = (String) entry.getKey();
                params.add(new KeyValueStore(name, (new StringBuilder()).append("${").append(name).append("}").toString()));
            } else {
                params.add(new KeyValueStore((String) entry.getKey(), entry.getValue()));
            }
        }

        List keyValueStoreList = ParameterUtils.replaceValueFromContext(suitContext, params);
        for (Iterator i$ = keyValueStoreList.iterator(); i$.hasNext(); ) {
            KeyValueStore keyValueStore = (KeyValueStore) i$.next();
            if ("[null]".equals(keyValueStore.getValue()))
                caseContext.addContext(keyValueStore.getName(), "null");
            else
                caseContext.addContext(keyValueStore.getName(), keyValueStore.getValue());
        }
    }

    private void addTestCase(List<FrameworkMethod> frameworkMethodList, List<TestCase> testCases) {
        if ((testCases == null) || (testCases.size() < 1)) {
            return;
        }
        for (TestCase testCase : testCases)
            frameworkMethodList.add(new ServertestFrameworkMethod(null, testCase, this.suitContext));
    }

    protected Statement methodInvoker(FrameworkMethod method, Object test) {
        if ((method instanceof ServertestFrameworkMethod)) {
            return new ServertestStatement((ServertestFrameworkMethod) method, this.reporter);
        }
        return super.methodInvoker(method, test);
    }

    private boolean isTestWithFile() {
        return (getTestClass().getJavaClass().getAnnotation(ServerTest.Options.class) != null) || (this.testSuite != null);
    }

    protected void runChild(FrameworkMethod method, RunNotifier notifier) {
        EachTestNotifier eachNotifier = makeNotifier(method, notifier);
        runNotIgnored(method, eachNotifier);
    }

    private EachTestNotifier makeNotifier(FrameworkMethod method, RunNotifier notifier) {
        Description description = describeChild(method);
        return new EachTestNotifier(notifier, description);
    }

    private void runNotIgnored(FrameworkMethod method, EachTestNotifier eachNotifier) {
        eachNotifier.fireTestStarted();
        try {
            Statement statement = methodBlock(method);
            statement.evaluate();
        } catch (AssumptionViolatedException e) {
            eachNotifier.addFailedAssumption(e);
        } catch (Throwable e) {
            eachNotifier.addFailure(e);
        } finally {
            eachNotifier.fireTestFinished();
        }
    }

    protected void validateInstanceMethods(List<Throwable> errors) {
    }

    protected String getName() {
        String desc = this.testSuite.getDesc();
        if (StringUtils.isBlank(desc)) {
            desc = this.testSuite.getId();
        }
        return desc + "-" + getShortFileName(this.testSuite.getCaseFileName());
    }

    private String getShortFileName(String fileName) {
        int i = fileName.lastIndexOf(File.separator);
        if (i < 0) {
            return fileName;
        }
        return fileName.substring(i + 1);
    }

    static {
        ClassLoader.getSystemClassLoader().setDefaultAssertionStatus(true);
    }
}
